package cn.wit.db.security;

import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.stream.Collectors;

import javax.crypto.SecretKey;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;

/**
 * 这是一个JWT功能的辅助类，主要用来生成token、解析token、验证token
 * 
 * @author hchaojie
 *
 */
@Component
public class JwtTokenProvider {
	private static final Logger logger = LoggerFactory.getLogger(JwtTokenProvider.class);
	
	@Autowired
	private JwtConfig jwtConfig;

	/**
	 * 从token里面解析出来用户名
	 * @param token
	 */
	public String getUserNameFromToken(String token) {
		Claims claims = Jwts.parser().setSigningKey(jwtConfig.getSecret().getBytes())
			.parseClaimsJws(token)
			.getBody();
		return claims.getSubject();
	}

	/**
	 * 从token里面解析出来用户名、角色
	 * @param token
	 */
	public UserDetails getUserDetailsFromToken(String token) {
		Claims claims = Jwts.parser().setSigningKey(jwtConfig.getSecret().getBytes())
			.parseClaimsJws(token)
			.getBody();
		
		UserDetails user = User.builder()
				.username(claims.getSubject())
				.password("")			// 要设置一个空的字符串，否则报错，实际上password用不上
				.authorities(AuthorityUtils.commaSeparatedStringToAuthorityList((String) claims.get("roles")))
				.build();
		
		return user;
	}
	
	public static void main(String[] args) throws UnsupportedEncodingException {
		String out = DigestUtils.md5DigestAsHex("passw0rd".getBytes());
		System.out.println(out);
	}

	/**
	 * 验证token是否有错
	 * 
	 * @param token
	 */
	public boolean validate(String token) {
		if (token == null || token.length() == 0) {
			return false;
		}

		try {
			Jwts.parser().setSigningKey(jwtConfig.getSecret().getBytes()).parseClaimsJws(token);
			
			return true;
		} catch (JwtException e) {
			logger.error("jwt 解析异常 {}", e.getMessage());
		}
		
		return false;
	}
	
	/**
	 * 从request对象里面解析出来token
	 */
	public String extractToken(HttpServletRequest request) {
		String bearerToken = request.getHeader(jwtConfig.getHeader());	// Authentication
		String prefix = jwtConfig.getPrefix();		// Bearer
		if (!StringUtils.isEmpty(bearerToken) && bearerToken.startsWith(prefix)) {
			return bearerToken.substring(prefix.length());
		}

		return null;	
	}
	
	/**
	 * 往response对象里面写jwt token
	 */
	public void insertAuthenticationToken(
			HttpServletResponse response, Authentication authentication) {
		// 根据用户信息生成token
		String token = generateToken(authentication);
		
		// 响应头里面写token
		response.addHeader(jwtConfig.getHeader(), jwtConfig.getPrefix() + token);
	}

	/* 根据用户信息生成jwt token  */
	private String generateToken(Authentication authc) {
		Date now = new Date();
		Date expration = new Date(now.getTime() + jwtConfig.getExpiration() * 1000);
		
		SecretKey key = Keys.hmacShaKeyFor(jwtConfig.getSecret().getBytes());
		
		User user = (User) authc.getPrincipal();
		String roles = authc.getAuthorities().stream().map(e -> e.getAuthority()).collect(Collectors.joining(","));
		logger.debug("roles:{}", roles);
		
		return Jwts.builder()
			.setSubject(user.getUsername())	// 把用户名放入token
			.setExpiration(expration)		// 过期时间
			.setIssuedAt(now)				// 发放时间
			.claim("roles", roles)			// 角色
			.signWith(key, SignatureAlgorithm.HS256)		// token加密方式
			.compact();
	}

}
